﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Warburton.FastBitArray;

namespace NUnitTests
{
    abstract public class BaseSuite
    {
        public static IList<int> Primes = (
            new List<int>
            {
                2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,
                73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,
                179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,
                283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,
                419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509
            }).AsReadOnly();

        private readonly IBitArrayFactory _factory;

        protected BaseSuite(IBitArrayFactory factory)
        {
            _factory = factory;
        }

        [Test]
        public void NumberOfBits()
        {
            var bitArray = _factory.BitArray(50);
            Assert.AreEqual(bitArray.NumberOfBits, 50);
        }

        [Test]
        public void ZeroLength()
        {
            var zeroLength = _factory.BitArray(0);
            Assert.AreEqual(0, zeroLength.NumberOfBits);
        }

        [Test]
        public void SimpleAnd()
        {
            var simple = _factory.BitArray(1);
            simple.Set(0, true);
            Assert.AreEqual(true, simple.Get(0));

            var other = _factory.BitArray(1);
            other.Set(0, true);
            Assert.AreEqual(true, other.Get(0));
            simple.And(other);
            Assert.AreEqual(true, simple.Get(0));
            other.Set(0, false);
            simple.And(other);
            Assert.AreEqual(false, simple.Get(0));
        }

        [Test]
        public void TestUpTo513Bits()
        {
            for (int i = 0; i <= 513; ++i)
            {
                for (int j = 0; j < Primes.Count; ++j)
                {
                    var array = _factory.BitArray(i);
                    var library = new System.Collections.BitArray(i);

                    int count = 0;
                    for (int k = 0; k < j && k < i; k += j)
                    {
                        ++count;
                        array.Set(k, true);
                        library.Set(k, true);
                    }

                    Assert.AreEqual(count, array.GetIndexes(true).Count());
                    for (int k = 0; k < i; ++k)
                        Assert.AreEqual(library.Get(k), array.Get(k));

                    for (int k = 0; k < j && k < i; k += j)
                    {
                        ++count;
                        array.Set(k, false);
                        library.Set(k, false);
                    }
                    Assert.AreEqual(0, array.GetIndexes(true).Count());
                    for (int k = 0; k < i; ++k)
                        Assert.AreEqual(false, array.Get(k));
                }
            }
        }

        private void TestLogicFunction(Func<IBitArray, IBitArray, IBitArray> forBitArray, Func<System.Collections.BitArray, System.Collections.BitArray, System.Collections.BitArray> forLibrary)
        {
            for (int i = 0; i <= 257; ++i)
            {
                var array1 = _factory.BitArray(i);
                var library1 = new System.Collections.BitArray(i);

                var l = 0;
                while (Primes[l] < i)
                {
                    array1.Set(Primes[l], true);
                    library1.Set(Primes[l], true);
                    ++l;
                }

                for (int j = 0; j < i; j++)
                {
                    var array2 = _factory.BitArray(i);
                    var library2 = new System.Collections.BitArray(i);

                    l = 0;
                    while (Primes[l] < i)
                    {
                        array1.Set(Primes[l],   !array1.Get(Primes[l]));
                        library1.Set(Primes[l], !library1.Get(Primes[l]));
                        ++l;
                    }

                    var test1 = forBitArray(array2, array1);
                    Assert.AreSame(test1, array2);

                    var test2 = forLibrary(library2, library1);
                    Assert.AreSame(test2, library2);

                    for (int k = 0; k < i; ++k)
                        Assert.AreEqual(array2.Get(k), library2.Get(k));
                }
            }
        }

        [Test]
        public void TestOr()
        {
            TestLogicFunction((lhs, rhs) => lhs.Or(rhs), (lhs, rhs) => lhs.Or(rhs));
        }

        [Test]
        public void TestAnd()
        {
            TestLogicFunction((lhs, rhs) => lhs.And(rhs), (lhs, rhs) => lhs.And(rhs));
        }

        [Test]
        public void TestXor()
        {
            TestLogicFunction((lhs, rhs) => lhs.Xor(rhs), (lhs, rhs) => lhs.Xor(rhs));
        }

        [Test]
        public void TestNot()
        {
            for (int i = 0; i <= 257; ++i)
            {
                var array1 = _factory.BitArray(i);
                var library1 = new System.Collections.BitArray(i);

                var l = 0;
                while (Primes[l] < i)
                {
                    array1.Set(Primes[l], true);
                    library1.Set(Primes[l], true);
                    ++l;
                }

                var test1 = array1.Not();
                Assert.AreSame(test1, array1);

                var test2 = library1.Not();
                Assert.AreSame(test2, library1);

                for (int j = 0; j < i; ++j)
                    Assert.AreEqual(array1.Get(j), library1.Get(j));
            }
        }

        [Test]
        public void TestSetAll()
        {
            for (int i = 0; i <= 257; ++i)
            {
                var array1 = _factory.BitArray(i);
                var library1 = new System.Collections.BitArray(i);

                array1.SetAll(true);
                library1.SetAll(true);

                for (int j = 0; j < i; ++j)
                    Assert.AreEqual(array1.Get(j), library1.Get(j));

                array1.SetAll(false);
                library1.SetAll(false);

                for (int j = 0; j < i; ++j)
                    Assert.AreEqual(array1.Get(j), library1.Get(j));
            }
        }
    }
}
